home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / More Source / C⁄C++ / Xconq 7.0d37 / source / curses / cdraw.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-04-06  |  27.9 KB  |  1,223 lines  |  [TEXT/KAHL]

  1. /* Map graphics for the curses interface to Xconq.
  2.    Copyright (C) 1986, 1987, 1988, 1989, 1991, 1992, 1993, 1994, 1995
  3.    Stanley T. Shebs.
  4.  
  5. Xconq is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2, or (at your option)
  8. any later version.  See the file COPYING.  */
  9.  
  10. #include "conq.h"
  11. #include "cconq.h"
  12.  
  13. static int xform PROTO ((int x, int y, int *sxp, int *syp));
  14. static void draw_divider PROTO ((void));
  15. static void draw_terrain_row PROTO ((int x, int y, int len));
  16. static void draw_units PROTO ((int x, int y));
  17. static void draw_people PROTO ((int x, int y));
  18. static void draw_legend PROTO ((int x, int y));
  19. static void draw_unit_details PROTO ((Unit *unit));
  20. static void describe_cell PROTO ((int x, int y, int tview, char *filler,
  21.                   char *buf));
  22. static void draw_unit_list_entry PROTO ((int line));
  23. static void draw_type_list_entry PROTO ((int line));
  24.  
  25. char *dashbuffer;
  26.  
  27. /* When true, draw the terrain character in both character positions
  28.    of a cell; otherwise draw in just the left position and put a blank
  29.    in the right position. */
  30.  
  31. int use_both_chars = TRUE;
  32.  
  33. /* When true, use standout mode to highlite mode lines and such.
  34.    Standout mode increases visual clutter somewhat, so it's not
  35.    obviously good or obviously bad. */
  36.  
  37. int use_standout = FALSE;
  38.  
  39. /* Completely redo a screen, making no assumptions about appearance.
  40.    This is a last-gasp measure; most redrawing should be restricted to
  41.    only the directly affected windows.  Also this shouldn't be done
  42.    without the user's permission, since it will blow away impending
  43.    input. */
  44.  
  45. void
  46. redraw()
  47. {
  48.     if (active_display(dside)) {
  49.     switch (mode) {
  50.       case SURVEY:
  51.       case MOVE:
  52.       case PROMPT:
  53.       case PROMPTXY:
  54.         clear();
  55.         draw_divider();
  56.         show_toplines();
  57.         show_game_date();
  58.         show_clock();
  59.         show_side_list();
  60.         show_list();
  61.         show_closeup();
  62.         show_map();
  63.         break;
  64.       case HELP:
  65.       case MORE:
  66.         show_help();
  67.         break;
  68.       default:
  69.         break;
  70.     }
  71.     show_cursor();
  72.     refresh();
  73.     }
  74. }
  75.  
  76. static void
  77. draw_divider()
  78. {
  79.     int i;
  80.  
  81.     for (i = 2; i < LINES; ++i) {
  82.     mvaddstr(i, mw, "|");
  83.     }
  84. }
  85.  
  86. /* Decide whether given location is not too close to edge of screen.
  87.    We do this because it's a pain to move units when half the adjacent
  88.    cells aren't even visible.  This routine effectively places a lower
  89.    limit of 5x5 for the map window. (I think) */
  90.  
  91. int
  92. in_middle(x, y)
  93. int x, y;
  94. {
  95.     int sx, sy;
  96.  
  97.     xform(x, y, &sx, &sy);
  98.     return ((between (3, sx, mw - 3) || !between(2, x, area.width-2))
  99.         && (between (3, sy, mh - 3) || !between(2, y, area.height-2)));
  100. }
  101.  
  102. static int
  103. xform(x, y, sxp, syp)
  104. int x, y, *sxp, *syp;
  105. {
  106.     xform_cell(mvp, x, y, sxp, syp);
  107. }
  108.  
  109. void
  110. set_scroll()
  111. {
  112.     int sx, sy, hexadj = hexagon_adjust(mvp);
  113.  
  114.     if (mw < (mvp->totsw - hexadj))
  115.       sx = max(sx, hexadj);
  116.     else
  117.       sx = hexadj;
  118.     if (mh >= mvp->totsh)
  119.       sy = 0;
  120.     set_view_position(mvp, sx, sy);
  121. }
  122.  
  123. void
  124. set_map_viewport()
  125. {
  126.     /* Compute the size of the viewport. */
  127.     vw = min(area.width, mw / 2 + 1);
  128.     vh = min(area.height, mh);
  129.     /* Compute the bottom visible row. */
  130.     vy = (mvp->totsh - mvp->sy) - vh;
  131.     /* Adjust to keep its value from being outside the area. */
  132.     vy = max(0, min(vy, area.height - vh));
  133.     /* Compute the first visible column. */
  134.     vx = mvp->sx / 2 - vy / 2 - 1;
  135.     DGprintf("Set %dx%d viewport at %d,%d\n", vw, vh, vx, vy);
  136. }
  137.  
  138. /* Display a map and all of its paraphernalia. */
  139.  
  140. void
  141. show_map()
  142. {
  143.     int y1, y2, y, x1, x2;
  144.     int halfheight = area.height / 2;
  145.  
  146.     clear_window(mapwin);
  147.     set_map_viewport();
  148.     /* Compute top and bottom rows to be displayed. */
  149.     y1 = min(vy + vh, area.height - 1);
  150.     y2 = vy;
  151.     for (y = y1; y >= y2; --y) {
  152.     /* Adjust the right and left bounds to fill the viewport as
  153.        much as possible, without going too far (the drawing code
  154.        will clip, but clipped drawing is still expensive). */
  155.     x1 = vx - (y - vy) / 2;
  156.     x2 = x1 + vw + 2;
  157.     /* If the area doesn't wrap, then we might have to stop
  158.        drawing before we reach the edge of the viewport. */
  159.     if (!area.xwrap) {
  160.         x1 = max(0, min(x1, area.width - 1));
  161.         x2 = max(0, min(x2, area.width));
  162.         if (x1 + y > area.width + halfheight)
  163.           continue;
  164.         if (x2 + y < halfheight)
  165.           continue;
  166.         if (x2 + y > area.width + halfheight)
  167.           x2 = area.width + halfheight - y;
  168.         if (x1 + y < halfheight)
  169.           x1 = halfheight - y;
  170.     }
  171.     draw_row(x1, y, x2 - x1);
  172.     }
  173.     /* Draw modeline in standout if desired and possible. */
  174.     if (use_standout)
  175.       standout();
  176.     strcpy(tmpbuf, "");
  177.     if (drawunits) {
  178.     strcat(tmpbuf, "units");
  179.     }
  180.     if (drawnames) {
  181.     if (strlen(tmpbuf) > 0)
  182.       strcat(tmpbuf, ",");
  183.     strcat(tmpbuf, "names");
  184.     }
  185.     if (drawpeople) {
  186.     if (strlen(tmpbuf) > 0)
  187.       strcat(tmpbuf, ",");
  188.     strcat(tmpbuf, "people");
  189.     }
  190.     memcpy(spbuf, dashbuffer, mw);
  191.     memcpy(spbuf+2, "Map", 3);
  192.     memcpy(spbuf+7, (mode == SURVEY ? "Survey" : "-Move-"), 6);
  193.     memcpy(spbuf+17, "(", 1);
  194.     memcpy(spbuf+18, tmpbuf, strlen(tmpbuf));
  195.     memcpy(spbuf+18+strlen(tmpbuf), ")", 1);
  196.     spbuf[mw] = '\0';
  197.     /* (should add followaction flag to status line?) */
  198.     draw_text(mapwin, 0, mh, spbuf);
  199.     if (use_standout)
  200.       standend();
  201. }
  202.  
  203. void
  204. draw_row(x0, y0, len)
  205. int x0, y0, len;
  206. {
  207.     int u, x;
  208.  
  209.     if (drawterrain) {
  210.     draw_terrain_row(x0, y0, len);
  211.     }
  212.     /* Draw sparse things on top of the basic row. */
  213.     for (x = x0; x < x0 + len; ++x) {
  214.     /* The relative ordering of these is quite important.  Note that
  215.        each should be prepared to run independently also, since the
  216.        other displays might have been turned off. */
  217.     if (people_sides_defined() && drawpeople) {
  218.         draw_people(x, y0);
  219.     }
  220.     if (drawunits) {
  221.         draw_units(x, y0);
  222.     }
  223.     }
  224.     /* Need a second loop so names are superimposed properly. */
  225.     for (x = x0; x < x0 + len; ++x) {
  226.     if (drawnames) {
  227.         draw_legend(x, y0);
  228.     }
  229.     }
  230. }
  231.  
  232. /* Draw a single row of just terrain. */
  233.  
  234. static void
  235. draw_terrain_row(x0, y0, len)
  236. int x0, y0, len;
  237. {
  238.     char ch, ch2;
  239.     int x, sx, sy, t, t2;
  240.  
  241.     xform(x0, y0, &sx, &sy);
  242.     for (x = x0; x < x0 + len; ++x) {
  243.     ch = ((x % 2 == 0 && y0 % 2 == 0) ? unseen_char_2 : unseen_char_1);
  244.     ch2 = ' ';
  245.     if (terrain_visible(x, y0)) {
  246.         t = terrain_at(x, y0);
  247.         ch = terrchars[t];
  248.         ch2 = (use_both_chars ? ch : ' ');
  249.         /* Just provide a hint at presence of aux terrain. */
  250.         /* (should precalc whether to do all this?) */
  251.         if (any_aux_terrain_defined()) {
  252.         for_all_terrain_types(t2) {
  253.             if (aux_terrain_defined(t2)) {
  254.             if (aux_terrain_at(x, y0, t2)) {
  255.                 ch2 = '#';
  256.                 break;
  257.             }
  258.             }
  259.         }
  260.         }
  261.     }
  262.     if (cur_at(mapwin, sx, sy))
  263.       addch(ch);
  264.     if (cur_at(mapwin, sx + 1, sy))
  265.       addch(ch2);
  266.     sx += 2;
  267.     }
  268. }
  269.  
  270. /* Draw a single unit char pair as appropriate. */
  271.  
  272. static void
  273. draw_units(x, y)
  274. int x, y;
  275. {
  276.     int draw = FALSE;
  277.     int sx, sy, uview, u, s;
  278.     Unit *unit;
  279.     
  280.     if (g_see_all()) {
  281.     if ((unit = unit_at(x, y)) != NULL) {
  282.         u = unit->type;
  283.         s = side_number(unit->side);
  284.         draw = TRUE;
  285.     }
  286.     } else if ((uview = unit_view(dside, wrapx(x), y)) != EMPTY) {
  287.     u = vtype(uview);
  288.     s = vside(uview);
  289.     draw = TRUE;
  290.     }
  291.     if (draw) {
  292.     xform(x, y, &sx, &sy);
  293.     if (cur_at(mapwin, sx, sy)) {
  294.         addch(unitchars[u]);
  295.         if (between(1, s, numsides))
  296.           addch(s + '0');
  297.     }
  298.     }
  299. }
  300.  
  301. /* Indicate what kind of people are living in the given cell. */
  302.  
  303. static void
  304. draw_people(x, y)
  305. int x, y;
  306. {
  307.     int pop, sx, sy;
  308.  
  309.     if (terrain_visible(x, y)
  310.     && (pop = people_side_at(wrapx(x), y)) != NOBODY) {
  311.     xform(x, y, &sx, &sy);
  312.     if (cur_at(mapwin, sx + 1, sy))
  313.       addch(pop + '0');
  314.     }
  315. }
  316.  
  317. /* Draw any text that should be associated with this cell. */
  318.  
  319. /* (could precompute what the string will lap over and move or truncate str),
  320.    should be deterministic for each mag, so redraw doesn't scramble */
  321.  
  322. /* do geofeatures, label at a cell with nothing else, and declared as the
  323.    feature's "center" */
  324.  
  325. static void
  326. draw_legend(x, y)
  327. int x, y;
  328. {
  329.     int sx, sy, pixlen;
  330.     char legend[100], *str;
  331.     int uview = unit_view(dside, x, y);
  332.     Feature *feature;
  333.     Unit *unit;
  334.  
  335.     if (uview == EMPTY)
  336.       return;
  337.     /* Draw a unit's name or number. */
  338.     /* (This will only do top unit in hex, what about others?) */
  339.     if (drawunits
  340.     && (unit = unit_at(x, y)) != NULL) {
  341.     /* still has a bug if types happen to match - need to use view date
  342.        instead? */
  343.     if (vtype(uview) != unit->type)
  344.       return;
  345.     if (unit->name != NULL) {
  346.         strcpy(legend, unit->name);
  347.     } else {
  348.         /* Turns out we have nothing to make a legend with. */
  349.         /* Note that, unlike some other interfaces, this one
  350.            does not ever display unit numbers, since it makes the
  351.            screen too cluttered to read. */
  352.         return;
  353.     }
  354.     xform(x, y, &sx, &sy);
  355.     draw_text(mapwin, sx + 2, sy, legend);
  356.     } else {
  357.     if ((feature = feature_at(x, y)) != NULL) {
  358.         if (feature->size == 1 && (str = feature_name_at(x, y)) != NULL) {
  359.         xform(x, y, &sx, &sy);
  360.         pixlen = strlen(str) / 2;
  361.         draw_text(mapwin, sx - pixlen, sy, str);
  362.         }
  363.     }
  364.     }
  365. }
  366.  
  367. /* (should blink the cursor or something to indicate a hit) */
  368.  
  369. void draw_blast PROTO ((Unit *unit, Side *es, int hit));
  370.  
  371. void
  372. draw_blast(unit, es, hit)
  373. Unit *unit;
  374. Side *es;
  375. int hit;
  376. {
  377. }
  378.  
  379. /* (should change text[12] to an array) */
  380.  
  381. void low_notify PROTO ((char *tmpnbuf));
  382.  
  383. /* General-purpose notification routine, puts message on top line
  384.    of the screen. */
  385.  
  386. void
  387. #ifdef __STDC__
  388. notify(Side *side, char *fmt, ...)
  389. #else
  390. notify(side, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9)
  391. Side *side;
  392. char *fmt;
  393. long a1, a2, a3, a4, a5, a6, a7, a8, a9;
  394. #endif
  395. {
  396.     char tmpnbuf[BUFSIZE], *lastblank;
  397.  
  398.     if (active_display(side)) {
  399. #ifdef __STDC__
  400.         {
  401.         va_list ap;
  402.  
  403.         va_start(ap, fmt);
  404.         vsprintf(tmpnbuf, fmt, ap);
  405.         va_end(ap);
  406.      }
  407. #else
  408.     sprintf(tmpnbuf, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9);
  409. #endif
  410.     low_notify(tmpnbuf);
  411.     }
  412. }
  413.  
  414. void
  415. low_notify(tmpnbuf)
  416. char *tmpnbuf;
  417. {
  418.     char *lastblank;
  419.  
  420.     /* Capitalize first char of notice. */
  421.     if (islower(tmpnbuf[0]))
  422.       tmpnbuf[0] = toupper(tmpnbuf[0]);
  423.     if (strlen(tmpnbuf) > toplineswin->w) {
  424.     if (lastblank = strchr(tmpnbuf + toplineswin->w - 10, ' ')) {
  425.         sprintf(text2, lastblank + 1);
  426.         *lastblank = '\0';
  427.     }
  428.     }
  429.     sprintf(text1, "%s", tmpnbuf);
  430.     DGprintf("%s\n", text1);
  431.     DGprintf("%s\n", text2);
  432.     show_toplines();
  433.     /* Put cursor back to where it was. */
  434.     show_cursor();
  435.     refresh();
  436. }
  437.  
  438. void
  439. show_toplines()
  440. {
  441.     clear_window(toplineswin);
  442.     draw_text(toplineswin, 0, 0, text1);
  443.     draw_text(toplineswin, 0, 1, text2);
  444. }
  445.  
  446. void
  447. clear_toplines()
  448. {
  449.     text1[0] = '\0';
  450.     text2[0] = '\0';
  451.     show_toplines();
  452. }
  453.  
  454. /* Display all the details of the currently-selected unit/cell. */
  455.  
  456. /* (much of this should be made into kernel routines shared by interfaces) */
  457.  
  458. void
  459. show_closeup()
  460. {
  461.     int u, tview, uview;
  462.     char *filler = "Empty ";
  463.     Unit *unit = NULL, *topunit;
  464.     Side *side2;
  465.  
  466.     clear_window(closeupwin);
  467.     if (inside_area(curx, cury)) {
  468.     if (terrain_visible(curx, cury)) {
  469.         tview = buildtview(terrain_at(curx, cury));
  470.     } else {
  471.         tview = terrain_view(dside, curx, cury);
  472.     }
  473.     if (units_visible(curx, cury)) {
  474.         unit = (in_play(curunit) ? curunit : unit_at(curx, cury));
  475.         if (in_play(unit)) {
  476.         /* If there is a unit there, we can at least see basic info. */
  477.         sprintf(tmpbuf, "%s", unit_handle(dside, unit));
  478.         draw_text(closeupwin, 0, 0, tmpbuf);
  479.         if (unit->side == dside) {
  480.             draw_unit_details(unit);
  481.         }
  482.         filler = "In ";
  483.         }
  484.     } else if ((uview = unit_view(dside, curx, cury)) != EMPTY) {
  485.         filler = "In ";
  486.         u = vtype(uview);
  487.         side2 = side_n(vside(uview));
  488.         sprintf(tmpbuf, "%s %s",
  489.             side_adjective(side2), u_type_name(u));
  490.         draw_text(closeupwin, 0, 0, tmpbuf);
  491.     }
  492.     /* Describe the cell here. */
  493.     describe_cell(curx, cury, tview, filler, tmpbuf);
  494.     draw_text(closeupwin, 0, 1, tmpbuf);
  495.     } else {
  496.     sprintf(tmpbuf, "??? Off-area %d,%d ???", curx, cury);
  497.     draw_text(closeupwin, 0, 1, tmpbuf);
  498.     }
  499. }
  500.  
  501. static void
  502. describe_cell(x, y, tview, filler, buf)
  503. int x, y, tview;
  504. char *filler, *buf;
  505. {
  506.     int t, t2, dir;
  507.     char *featname;
  508.  
  509.     if (tview != UNSEEN) {
  510.     t = vterrain(tview);
  511.     /* Now describe terrain and position. */
  512.     sprintf(buf, "%s%s", filler, t_type_name(t));
  513.     if ((featname = feature_name_at(x, y)) != NULL) {
  514.         strcat(buf, " ");  strcat(buf, featname);
  515.     }
  516.     elevation_desc(buf+strlen(buf), x, y);
  517.     /* (should put all weather on own line) */
  518.     temperature_desc(buf+strlen(buf), x, y);
  519.     /* add clouds and winds desc here */
  520.     linear_desc(buf+strlen(buf), x, y);
  521.     } else {
  522.     sprintf(buf, "<unknown>");
  523.     }
  524.     tprintf(buf, " at %d,%d", x, y);
  525. }
  526.  
  527. /* Describe the state of the given unit, in maximal detail. */
  528.  
  529. static void
  530. draw_unit_details(unit)
  531. Unit *unit;
  532. {
  533.     int i, u = unit->type, m, nums[MAXUTYPES], xpos;
  534.     int terr = terrain_at(unit->x, unit->y);
  535.     Unit *occ, *top;
  536.  
  537.     /* Say which unit this is. */
  538.     sprintf(spbuf, "%s", unit_handle(dside, unit));
  539.     /* Describe the "important" parameters like hit points and moves. */
  540.     strcat(spbuf, "  ");
  541.     hp_desc(tmpbuf, unit, TRUE);
  542.     strcat(spbuf, tmpbuf);
  543.     /* (should say something about parts here) */
  544.     strcat(spbuf, "  ");
  545.     acp_desc(tmpbuf, unit, TRUE);
  546.     strcat(spbuf, tmpbuf);
  547.     draw_text(closeupwin, 0, 0, spbuf);
  548.     /* Mention transport and other units stacked here. */
  549.     if (unit->transport != NULL) {
  550.     sprintf(spbuf, "In %s", short_unit_handle(unit->transport));
  551.     } else {
  552.     describe_cell(unit->x, unit->y, buildtview(terr), "In ", spbuf);
  553.     /* make this a "stacked_at" macro? */
  554.     if (((top = unit_at(unit->x, unit->y)) != NULL)
  555.         && top->nexthere != NULL) {
  556.         strcat(spbuf, ", ");
  557.         for_all_unit_types(i) nums[i] = 0;
  558.         for_all_stack(unit->x, unit->y, occ) ++nums[occ->type];
  559.         --nums[u];  /* don't count ourself */
  560.         for_all_unit_types(i) {
  561.         if (nums[i] > 0) {
  562.             sprintf(tmpbuf, "%d %1s  ", nums[i], utype_name_n(i, 1));
  563.             strcat(spbuf, tmpbuf);
  564.         }
  565.         }
  566.         strcat(spbuf, " here also");
  567.     }
  568.     }
  569.     draw_text(closeupwin, 0, 1, spbuf);
  570.     /* Very briefly list the numbers and types of the occupants. */
  571.     sprintf(spbuf, "");
  572.     if (unit->occupant != NULL) {
  573.     strcpy(spbuf, "Occ ");
  574.     /* (should be occ_desc in nlang.c?) */
  575.     for_all_unit_types(i) nums[i] = 0;
  576.     for_all_occupants(unit, occ) ++nums[occ->type];
  577.     for_all_unit_types(i) {
  578.         if (nums[i] > 0) {
  579.         sprintf(tmpbuf, "%d %1s  ", nums[i], utype_name_n(i, 1));
  580.         strcat(spbuf, tmpbuf);
  581.         }
  582.     }
  583.     }
  584.     draw_text(closeupwin, 0, 2, spbuf);
  585.     /* Describe the state of all the supplies. */
  586.     /* (should be supply_desc in kernel?) */
  587.     sprintf(spbuf, "");
  588.     for_all_material_types(m) {
  589.     if (um_storage_x(u, m) > 0) {
  590.         sprintf(tmpbuf, "%s %d/%d  ",
  591.             m_type_name(m), unit->supply[m], um_storage_x(u, m));
  592.         strcat(spbuf, tmpbuf);
  593.     }
  594.     }
  595.     draw_text(closeupwin, 0, 3, spbuf);
  596.     /* Describe the current plans, tasks, etc. */
  597.     /* (needs much improvement) */
  598.     if (unit->plan) {
  599.     plan_desc(spbuf, unit);
  600.     draw_text(closeupwin, 0, 4, spbuf);
  601.     }
  602. }
  603.  
  604. /* Basic routine that displays the list of sides. */
  605.  
  606. void
  607. show_side_list()
  608. {
  609.     char ismoving, progress[20], *dpyname;
  610.     int sy = 0, totacp;
  611.     Side *side2;
  612.     extern int curpriority;
  613.  
  614.     /* Ensure subwin is clear. */
  615.     clear_window(sideswin);
  616.     for_all_sides(side2) {
  617.         ismoving = ' ';
  618.     if ((g_use_side_priority()
  619.          ? (curpriority == side2->priority)
  620.          : (!side2->finishedturn)))
  621.       ismoving = '*';
  622.     if (side2->designer) {
  623.         strcpy(progress, "DESIGN  ");
  624.     } else if (side2->ingame) {
  625.         totacp = side_initacp(side2);
  626.         if (totacp > 0) {
  627.         sprintf(progress, "%3d%%", (100 * side_acp(side2)) / totacp);
  628.         /* We get to see our actual total acp as well. */
  629.         if (dside == side2) {
  630.             tprintf(progress, "/%-3d", totacp);
  631.         } else {
  632.             strcat(progress, "    ");
  633.         }
  634.         } else {
  635.         strcpy(progress, "   --   ");
  636.         }
  637.     } else {
  638.         if (side2->lost) {
  639.         strcpy(progress, " Lost   ");
  640.         } else if (side_won(side2)) {
  641.         strcpy(progress, " Won!   ");
  642.         } else {
  643.         strcpy(progress, " Gone   ");
  644.         }
  645.     }
  646.     dpyname = "";
  647.     if (side2->player->displayname)
  648.       dpyname = side2->player->displayname;
  649.     sprintf(spbuf, "%d%c %s %s (%s)",
  650.         side_number(side2), ismoving, progress,
  651.         side_name(side2), dpyname);
  652.     draw_text(sideswin, 0, sy, spbuf);
  653.     sy += 1;
  654.     }
  655.     /* Draw the modeline. */
  656.     if (use_standout)
  657.       standout();
  658.     memcpy(spbuf, dashbuffer, lw);
  659.     memcpy(spbuf+1, "Sides", 5);
  660.     spbuf[lw] = '\0';
  661.     draw_text(sideswin, 0, sh - 1, spbuf);
  662.     if (use_standout)
  663.       standend();
  664. }
  665.  
  666. /* Display the date. */
  667.  
  668. void
  669. show_game_date()
  670. {
  671. #if 0
  672.     clear_window(datewin);
  673.     /* First line of the game state. */
  674.     /* (should cache this date string a la Mac version?) */
  675.     sprintf(spbuf, "%s", absolute_date_string(g_turn()));
  676.     draw_text(datewin, 0, 0, spbuf);
  677.     /* (should use second line for something) */
  678.     refresh();
  679. #endif
  680. }
  681.  
  682. /* General list display routine. */
  683.  
  684. /* (should track beginning/end of displayed list, draw only visible elts) */
  685. /* (should add scrolling interaction) */
  686.  
  687. int firstvisible = 0;
  688. int lastvisible = 30;
  689. UnitVector *listvector = NULL;
  690. int listnumunits = 0;
  691.  
  692. static void organize_list_contents PROTO ((void));
  693. static void add_unit_to_list PROTO ((Unit *unit));
  694.  
  695. static void
  696. organize_list_contents()
  697. {
  698.     Side *side2;
  699.     Unit *unit;
  700.  
  701.     /* Build up the array of units for this list. */
  702.     listnumunits = 0;
  703.     clear_unit_vector(listvector);
  704.     /* We always see our own units. */
  705.     for_all_side_units(dside, unit) {
  706.     add_unit_to_list(unit);
  707.     }
  708.     for_all_sides(side2) {
  709.     if (dside != side2) {
  710.         for_all_side_units(side2, unit) {
  711.         if (side_sees_image(dside, unit)) {
  712.             add_unit_to_list(unit);
  713.         }
  714.         }
  715.     }
  716.     }
  717.     for_all_side_units(indepside, unit) {
  718.     if (side_sees_image(dside, unit)) {
  719.         add_unit_to_list(unit);
  720.     }
  721.     }
  722.     /* Now sort the list according to its keys. */
  723.     sort_unit_vector(listvector);
  724. }
  725.  
  726. static void
  727. add_unit_to_list(unit)
  728. Unit *unit;
  729. {
  730.     if (alive(unit)) {
  731.     add_unit_to_vector(listvector, unit, FALSE);
  732.     /* (should apply other inclusion criteria too?) */
  733.     ++listnumunits;
  734.     }
  735. }
  736.  
  737. int listtype = 0;
  738.  
  739. void
  740. show_list()
  741. {
  742.     int i = 0, line = 1, u;
  743.     char *maincat = "xxx", *filter = "yyy";
  744.     Unit *unit;
  745.     Side *loopside, *side2;
  746.  
  747.     clear_window(listwin);
  748.     switch (listtype) {
  749.       case 0:
  750.     if (listvector == NULL) {
  751.         listvector = make_unit_vector(1000);
  752.         listnumunits = 0;
  753.         for (i = 0; i < MAXSORTKEYS; ++i) {
  754.         tmpsortkeys[i] = bynothing;
  755.         }
  756.         tmpsortkeys[0] = byside;
  757.         organize_list_contents();
  758.     }
  759.     for (line = firstvisible; line <= lastvisible; ++line) {
  760.         draw_unit_list_entry(line);
  761.     }
  762.     maincat = "Units";
  763.     switch (listsides) {
  764.       case ourside:
  765.         filter = "-Own--";
  766.         break;
  767.       case ourallies:
  768.         filter = "Allied";
  769.         break;
  770.       case allsides:
  771.         filter = "-ALL--";
  772.         break;
  773.     }
  774.     break;
  775.       case 1:
  776.     for_all_unit_types(line) {
  777.         draw_type_list_entry(line);
  778.     } 
  779.     maincat = "Types";
  780.     filter = "------";
  781.     }
  782.     tmpbuf[0] = '\0';
  783.     for (i = 0; i < MAXSORTKEYS; ++i) {
  784.     if (i == 0) {
  785.         strcat(tmpbuf, "by ");
  786.     } else if (tmpsortkeys[i] != bynothing) {
  787.         strcat(tmpbuf, ",");
  788.     }
  789.     switch (tmpsortkeys[i]) {
  790.       case byside:
  791.         strcat(tmpbuf, "side");
  792.         break;
  793.       case bynothing:
  794.         break;
  795.       default:
  796.         strcat(tmpbuf, "???");
  797.         break;
  798.     }
  799.     }
  800.     /* Draw the modeline, in standout if possible. */
  801.     if (use_standout)
  802.       standout();
  803.     memcpy(spbuf, dashbuffer, lw);
  804.     memcpy(spbuf+1, maincat, 5);
  805.     memcpy(spbuf+7, filter, 6);
  806.     memcpy(spbuf+14, tmpbuf, strlen(tmpbuf));
  807.     spbuf[lw] = '\0';
  808.     draw_text(listwin, 0, lh - 1, spbuf);
  809.     if (use_standout)
  810.       standend();
  811.     refresh();
  812. }
  813.  
  814. void
  815. cycle_list_type()
  816. {
  817.     listtype = (listtype + 1) % 2;
  818. }
  819.  
  820. void
  821. cycle_list_filter()
  822. {
  823.     listsides = (listsides + 1) % 3;
  824. }
  825.  
  826. void
  827. cycle_list_order()
  828. {
  829.     tmpsortkeys[0] = (tmpsortkeys[0] + 1) % numsortkeytypes;
  830.     sort_unit_vector(listvector);
  831. }
  832.  
  833. /* Alter the numbers for a single type of unit.  Should be called right
  834.    after any changes.  Formatted to look nice, but kind of messy to set
  835.    up correctly; display should not jump back and forth as the numbers
  836.    change in size. */
  837.  
  838. int firsttypevisible = 0;
  839. int lasttypevisible = MAXUTYPES;
  840.  
  841. static void
  842. draw_type_list_entry(line)
  843. int line;
  844. {
  845.     int u, num;
  846.  
  847.     u = line + firsttypevisible;
  848.     if (!between(0, u, numutypes))
  849.       return;
  850.     if (u > lasttypevisible)
  851.       return;
  852.     sprintf(spbuf, " %c ", unitchars[u]);
  853.     /* Our unit total (right-justified) */
  854.     num = num_units_in_play(dside, u);
  855.     if (num > 0)    {
  856.     sprintf(tmpbuf, "%4d", num);
  857.     } else {
  858.     sprintf(tmpbuf, "    ");
  859.     }
  860.     strcat(spbuf, tmpbuf);
  861.     /* Our units under construction (left-justified) */
  862.     num = num_units_incomplete(dside, u);
  863.     if (num > 0) {
  864.     sprintf(tmpbuf, "(%d)", num);
  865.     } else {
  866.     sprintf(tmpbuf, "    ");
  867.     }
  868.     strcat(spbuf, tmpbuf);
  869.     draw_text(listwin, 1, line, spbuf);
  870. }
  871.  
  872. /* (should add these back in somewhere?) */
  873. #if 0
  874.     /* Our total possessions over the game */
  875.     /* Make sure that subsequent write goes into valid string area. */
  876.     strcat(spbuf,  "            ");
  877.     if (total_gain(side, u) > 0) {
  878.         sprintf(tmpbuf, "%4d", total_gain(side, u));
  879.         sprintf(spbuf+10, "%s", tmpbuf);
  880.     }
  881.     /* Our total losses over the game */
  882.     strcat(spbuf,  "            ");
  883.     if (total_loss(side, u) > 0) {
  884.         sprintf(tmpbuf, "- %d", total_loss(side, u));
  885.         sprintf(spbuf+15, "%s", tmpbuf);
  886.     }
  887. #endif
  888.  
  889. static void
  890. draw_unit_list_entry(line)
  891. int line;
  892. {
  893.     Unit *unit = listvector->units[line].unit;
  894.     char tmpbuf[BUFSIZE];
  895.  
  896.     if (unit == NULL) return;
  897.     if (unit == curunit) {
  898.     draw_text(listwin, 0, line, "*");
  899.     }
  900.     if (alive(unit)) {
  901.     name_or_number(unit, tmpbuf);
  902.     sprintf(spbuf, "%c%d %-16s ",
  903.         unitchars[unit->type], side_number(unit->side), tmpbuf);
  904.     if (unit->act && unit->act->acp > 0) {
  905.         tprintf(spbuf, "%3d", unit->act->acp);
  906.     }
  907.     if (unit->plan && unit->plan->asleep) {
  908.         strcat(spbuf, "z");
  909.     }
  910.     if (unit->plan && unit->plan->reserve) {
  911.         strcat(spbuf, "r");
  912.     }
  913.     if (unit->plan && unit->plan->waitingfortasks) {
  914.         strcat(spbuf, "w");
  915.     }
  916.     /* do hp also? */
  917.     } else {
  918.     sprintf(spbuf, "--");
  919.     }
  920.     draw_text(listwin, 1, line, spbuf);
  921. }
  922.  
  923. void
  924. show_clock()
  925. {
  926. #if 0
  927.     int time = 0;
  928.  
  929.     if (realtime_game()) {
  930.     time_desc(spbuf, time);
  931.     draw_text(clockwin, 0, 0, spbuf);
  932.     }
  933. #endif
  934. }
  935.  
  936. /* General window clearing. */
  937.  
  938. void
  939. clear_window(win)
  940. struct ccwin *win;
  941. {
  942.     int i;
  943.  
  944.     if (win->x == 0
  945.     && win->y == 0
  946.     && win->w == COLS
  947.     && win->h == LINES) {
  948.     clear();
  949.     } else if (between(0, win->x, COLS-1)
  950.            && between(0, win->w, COLS-1)
  951.            && between(0, win->y, LINES-1)
  952.            && between(0, win->h, LINES-1)) {
  953.     for (i = 0; i < win->w; ++i)
  954.       tmpbuf[i] = ' ';
  955.     tmpbuf[win->w] = '\0';
  956.     for (i = 0; i < win->h; ++i)
  957.       mvaddstr(win->y + i, win->x, tmpbuf);
  958.     } else {
  959.     printf("error: win %d is %dx%d @ %d,%d\n",
  960.            win, win->w, win->h, win->x, win->y);
  961.     }
  962. }
  963.  
  964. /* Draw a large blot over the area. */
  965.  
  966. void draw_mushroom PROTO ((int x, int y, int i));
  967.  
  968. void
  969. draw_mushroom(x, y, i)
  970. int x, y, i;
  971. {
  972.     int sx, sy;
  973.  
  974.     xform(x, y, &sx, &sy);
  975.     if (cur_at(mapwin, sx, sy)) {
  976.     addstr("##");
  977.     refresh();
  978.     if (i > 0) {
  979.         if (cur_at(mapwin, sx-1, sy+1))
  980.           addstr("####");
  981.         if (cur_at(mapwin, sx-2, sy))
  982.           addstr("######");
  983.         if (cur_at(mapwin, sx-1, sy-1))
  984.           addstr("####");
  985.         refresh();
  986.     }
  987.     }
  988. }
  989.  
  990. /* Drawing text is easy, but we do need to do clipping manually. */
  991.  
  992. int
  993. draw_text(win, x, y, str)
  994. struct ccwin *win;
  995. int x, y;
  996. char *str;
  997. {
  998.     int i, slen, linestart = 0;
  999.  
  1000.     if (y < 0)
  1001.       y = win->h - y;
  1002.     if (cur_at(win, x, y)) {
  1003.     slen = strlen(str);
  1004.     for (i = 0; i < slen; ++i) {
  1005.         if (str[i] == '\n') {
  1006.         if (y < win->h) {
  1007.             if (cur_at(win, x, ++y))
  1008.               linestart = i;
  1009.             else
  1010.               break;
  1011.         } else {
  1012.             /* Ran out of room */
  1013.             return i;
  1014.         }
  1015.         } else if (x + (i - linestart) < win->w) {
  1016.         addch(str[i]);
  1017.         }
  1018.     }
  1019.     }
  1020.     return (-1);
  1021. }
  1022.  
  1023. /* Make a beep by writing ^G. */
  1024.  
  1025. void
  1026. xbeep()
  1027. {
  1028. #ifdef THINK_C /* the portable library already includes a beep function */
  1029.     beep();
  1030. #else
  1031.     putchar('\007');
  1032. #endif
  1033. }
  1034.  
  1035. /* Put the current view into a file. */
  1036. /* (should be in kernel) */
  1037. /* (should be intelligent enough to cut into pages, or else document
  1038.    how to do it) */
  1039. /* (maybe display names too somehow, perhaps as second layer?) */
  1040. /* (should share with basic row layout, so consistent with screen) */
  1041.  
  1042. void
  1043. dump_text_view(side)
  1044. Side *side;
  1045. {
  1046.     char ch1, ch2;
  1047.     int x, y, t, uview, u, s, draw, i, vs;
  1048.     Side *side2;
  1049.     Unit *unit;
  1050.     FILE *fp;
  1051.  
  1052.     fp = fopen(VIEWFILE, "w");
  1053.     if (fp != NULL) {
  1054.     for (y = area.height-1; y >= 0; --y) {
  1055.         for (i = 0; i < y; ++i)
  1056.           fputc(' ', fp);
  1057.         for (x = 0; x < area.width; ++x) {
  1058.         ch1 = ch2 = ' ';
  1059.         if (terrain_visible(x, y)) {
  1060.             t = terrain_at(x, y);
  1061.             ch1 = terrchars[t];
  1062.             ch2 = (use_both_chars ? ch1 : ' ');
  1063.             draw = FALSE;
  1064.             if (g_see_all()) {
  1065.             if ((unit = unit_at(x, y)) != NULL) {
  1066.                 u = unit->type;
  1067.                 s = side_number(unit->side);
  1068.                 draw = TRUE;
  1069.             }
  1070.             } else if ((uview = unit_view(dside, wrapx(x), y)) != EMPTY) {
  1071.             u = vtype(uview);
  1072.             s = vside(uview);
  1073.             draw = TRUE;
  1074.             }
  1075.             if (draw) {
  1076.             ch1 = unitchars[u];
  1077.             side2 = side_n(vside(uview));
  1078.             ch2 = (side2 ? ((side == side2) ? ' ' : s + '0') : '`');
  1079.             }
  1080.         }
  1081.         fputc(ch1, fp);
  1082.         fputc(ch2, fp);
  1083.         }
  1084.         fprintf(fp, "\n");
  1085.     }
  1086.     fclose(fp);
  1087.     notify(side, "Dumped area view to \"%s\".", VIEWFILE);
  1088.     } else {
  1089.     notify(side, "Can't open \"%s\"!!", VIEWFILE);
  1090.     }
  1091. }
  1092.  
  1093. /* (should break helpstring into lines before displaying, so scrolling
  1094.    calcs simpler) */
  1095.  
  1096. int first_visible_help_pos;
  1097. int last_visible_help_pos;
  1098.  
  1099. void
  1100. show_help()
  1101. {
  1102.     int i, slen, x = 0, y = 0, more = FALSE;
  1103.     char *str = helpstring;
  1104.  
  1105.     clear();
  1106.     cur_at(helpwin, 0, 0);
  1107.     if (use_standout)
  1108.       standout();
  1109.     addstr(helptopic);
  1110.     if (use_standout)
  1111.       standend();
  1112.     cur_at(helpwin, 0, 1);
  1113.     slen = strlen(str);
  1114.     for (i = first_visible_help_pos; i < slen; ++i) {
  1115.     if (str[i] == '\n' || x > helpwin->w - 2) {
  1116.         if (y < helpwin->h - 2) {
  1117.         cur_at(helpwin, 0, ++y);
  1118.         x = 0;
  1119.         if (x > helpwin->w - 2) {
  1120.             addch(str[i]);
  1121.             ++x;
  1122.         }
  1123.         } else {
  1124.         more = TRUE;
  1125.         last_visible_help_pos = i;
  1126.         break;
  1127.         }
  1128.     } else {
  1129.         addch(str[i]);
  1130.         ++x;
  1131.     }
  1132.     }
  1133.     cur_at(helpwin, (more ? 9 : 0), helpwin->h - 1);
  1134.     addstr(" ['n' for next, 'p' for prev, 'q' to end]");
  1135.     if (more) {
  1136.     cur_at(helpwin, 0, helpwin->h - 1);
  1137.     standout();
  1138.     addstr("--More--");
  1139.     standend();
  1140.     }
  1141.     cur_at(helpwin, (more ? 8 : 0), helpwin->h - 1);
  1142. }
  1143.  
  1144. /* Put the terminal's cursor at an appropriate place. */
  1145.  
  1146. void
  1147. show_cursor()
  1148. {
  1149.     int sx, sy;
  1150.  
  1151.     if (active_display(dside)) {
  1152.     switch (mode) {
  1153.       case SURVEY:
  1154.       case MOVE:
  1155.         if (curunit != NULL
  1156.         && in_play(curunit)
  1157.         && !(curunit->x == curx && curunit->y == cury)) {
  1158.         curx = curunit->x;  cury = curunit->y;
  1159.         }
  1160.         if (!in_middle(curx, cury)) {
  1161.         set_view_focus(mvp, curx, cury);
  1162.         center_on_focus(mvp);
  1163.         set_map_viewport();
  1164.         show_map();
  1165.         }
  1166.         xform(curx, cury, &sx, &sy);
  1167.         if (!cur_at(mapwin, sx, sy))
  1168.           abort();
  1169.         break;
  1170.       case HELP:
  1171.         cur_at(helpwin, 0, 0);
  1172.         break;
  1173.       case MORE:
  1174.         xbeep();
  1175.         break;
  1176.       case PROMPT:
  1177.         /* This doesn't account for two-line prompts. */
  1178.         if (!cur_at(toplineswin, strlen(text1), 0))
  1179.           abort();
  1180.         break;
  1181.       case PROMPTXY:
  1182.         if (!in_middle(curx, cury)) {
  1183.         set_view_focus(mvp, curx, cury);
  1184.         center_on_focus(mvp);
  1185.         set_map_viewport();
  1186.         show_map();
  1187.         }
  1188.         xform(curx, cury, &sx, &sy);
  1189.         if (!cur_at(mapwin, sx, sy))
  1190.           abort();
  1191.         break;
  1192.       default:
  1193.         abort();
  1194.     }
  1195.     refresh();
  1196.     }
  1197. }
  1198.  
  1199. /* Position the cursor, being careful to test for sensibility. */
  1200.  
  1201. int
  1202. cur_at(win, x, y)
  1203. struct ccwin *win;
  1204. int x, y;
  1205. {
  1206.     int sx, sy;
  1207.  
  1208.     if (x < 0 || x >= win->w || y < 0 || y >= win->h) {
  1209.     /* Just return false if something is wrong; the caller should
  1210.        test this and react apropriately. */
  1211.     return FALSE;
  1212.     } else {
  1213.     sx = win->x + x;  sy = win->y + y;
  1214.     if (between(0, sx, COLS-1) && between(0, sy, LINES-1)) {
  1215.         move(sy, sx);
  1216.     } else {
  1217.         /* Bad. Very bad. */
  1218.         abort();
  1219.     }
  1220.     return TRUE;
  1221.     }
  1222. }
  1223.